spring security FormLoginConfigure的作用和源码解读

您所在的位置:网站首页 init方法的作用 java spring security FormLoginConfigure的作用和源码解读

spring security FormLoginConfigure的作用和源码解读

2023-04-12 20:01| 来源: 网络整理| 查看: 265

这一节来研究下spring security中FormLoginConfigurer这个配置器的作用

一、综述

FormLoginConfigurer 本质上还是一个SecurityConfigurer,用来对HttpSecurity这个构建器进行配置,它用来对表单登录的功能进行配置,通过HttpSecurity#formLogin()方法来给HttpSecurity 对象中添加此配置器,也就是添加表单登录的功能

二、构造方法

先看下构造方法

public FormLoginConfigurer() { // 创建了一个UsernamePasswordAuthenticationFilter,传给了父类构造方法 super(new UsernamePasswordAuthenticationFilter(), null); //设置登录时接收用户名的参数名 usernameParameter("username"); //设置登录时接收密码的参数名 passwordParameter("password"); } //上边的usernameParameter,passwordParameter可以用来调整登录时的参数名

接下来看下父类AbstractAuthenticationFilterConfigurer的构造方法

private F authFilter;//这个过滤器最后会被添加到DefaultSecurityFilterChain中用来处理表单登录请求 protected AbstractAuthenticationFilterConfigurer(F authenticationFilter, String defaultLoginProcessingUrl) { //调自己的无参数构造方法 this(); //把子类中new出的UsernamePasswordAuthenticationFilter赋值给了类中的属性authFilter //这个过滤器最后会被添加到DefaultSecurityFilterChain中用来处理表单登录请求 this.authFilter = authenticationFilter; //这里是在处理接收登录请求的url if (defaultLoginProcessingUrl != null) { loginProcessingUrl(defaultLoginProcessingUrl); } }

再看下AbstractAuthenticationFilterConfigurer的的无参数构造方法

private String loginPage; private String loginProcessingUrl; protected AbstractAuthenticationFilterConfigurer() { //设置了登录页面的访问地址为/login setLoginPage("/login"); } private void setLoginPage(String loginPage) { //对loginPage这个类属性赋默认值 /login this.loginPage = loginPage; this.authenticationEntryPoint = new LoginUrlAuthenticationEntryPoint(loginPage); } 三、loginPage和loginProcessingUrl方法

loginPage方法用来自定义登录页面,loginProcessingUrl用来指定登录请求的url

public FormLoginConfigurer loginPage(String loginPage) { //调用父类的方法 return super.loginPage(loginPage); } public T loginProcessingUrl(String loginProcessingUrl) { this.loginProcessingUrl = loginProcessingUrl; //这样设置以后这个过滤器也就是UsernamePasswordAuthenticationFilter匹配到请求路径 //是loginProcessingUrl就会把这次请求当做登录来进行处理 authFilter .setRequiresAuthenticationRequestMatcher(createLoginProcessingUrlMatcher(loginProcessingUrl)); return getSelf(); }

父类AbstractAuthenticationFilterConfigurer#loginPage

protected T loginPage(String loginPage) { setLoginPage(loginPage); //更新认证相关的一些默认值 updateAuthenticationDefaults(); this.customLoginPage = true; return getSelf(); } protected final void updateAuthenticationDefaults() { if (loginProcessingUrl == null) { //如果没有指定loginProcessingUrl就把loginPage作为它 //所以结合上边的构造方法可以知道默认情况下使用表单登录时loginProcessingUrl //和loginPage都是 /login loginProcessingUrl(loginPage); } if (failureHandler == null) { //设置登录失败的url failureUrl(loginPage + "?error"); } final LogoutConfigurer logoutConfigurer = getBuilder().getConfigurer( LogoutConfigurer.class); if (logoutConfigurer != null && !logoutConfigurer.isCustomLogoutSuccess()) { //退出的Url logoutConfigurer.logoutSuccessUrl(loginPage + "?logout"); } }

根据上边的源码可以得出一个重要的结论,如果自定义了loginPage就一定也要自定义loginProcessingUrl,

因为如果你只自定义loginPage那么loginProcessingUrl就会等于loginPage,加入你的loginPage="/login.html",显然这个地址是不能处理登录的。

四、init方法

因为FormLoginConfigurer 本质上还是一个SecurityConfigurer,所以它会有一个init方法来进行初始化。

@Override public void init(H http) throws Exception { //调用父类的init方法 super.init(http); //初始化生成登录页面的过滤器 initDefaultLoginFilter(http); } private void initDefaultLoginFilter(H http) { //spring security中哪个默认的登录页面就是通过这个过滤器生成的,这里是从共享对象中获取它, //它是在DefaultLoginPageConfigurer这个配置器中添加到共享对象中的。 //共享对象是spring security定义的,用来在配置过程中共享对象的。 DefaultLoginPageGeneratingFilter loginPageGeneratingFilter = http .getSharedObject(DefaultLoginPageGeneratingFilter.class); //底下就是在把通过FormLoginConfigurer设置的一些属性设置到过滤器中,用来生成默认登录页面 //例如username,password肯定会被作为登录页面中的表单属性。 if (loginPageGeneratingFilter != null && !isCustomLoginPage()) { loginPageGeneratingFilter.setFormLoginEnabled(true); loginPageGeneratingFilter.setUsernameParameter(getUsernameParameter()); loginPageGeneratingFilter.setPasswordParameter(getPasswordParameter()); loginPageGeneratingFilter.setLoginPageUrl(getLoginPage()); loginPageGeneratingFilter.setFailureUrl(getFailureUrl()); loginPageGeneratingFilter.setAuthenticationUrl(getLoginProcessingUrl()); } }

继续看下父类的init方法

@Override //这个方法就是设置了一些默认值 public void init(B http) throws Exception { //设置认证相关的默认值 updateAuthenticationDefaults(); updateAccessDefaults(http); registerDefaultAuthenticationEntryPoint(http); } protected final void updateAuthenticationDefaults() { // 如果url为空就设置这个url if (loginProcessingUrl == null) { //url为空就设置成loginPage也就是上边构造方法传的 /login //如果你仅自定义了loginPage,执行到这里就会造成loginPage==loginProcessingUrl //所以loginPage,loginProcessingUrl这两个要一起自定义 loginProcessingUrl(loginPage); } //登录失败处理器的默认值设置 if (failureHandler == null) { failureUrl(loginPage + "?error"); } //logout配置 final LogoutConfigurer logoutConfigurer = getBuilder().getConfigurer( LogoutConfigurer.class); //如果有logout配置,并且不是用户自定义的,就设置logoutSuccessUrl if (logoutConfigurer != null && !logoutConfigurer.isCustomLogoutSuccess()) { logoutConfigurer.logoutSuccessUrl(loginPage + "?logout"); } } 五、configure方法

因为FormLoginConfigurer 本质上还是一个SecurityConfigurer,所以它或者它的父类会有一个configure方法

AbstractAuthenticationFilterConfigurer#configure

//这个方法用来对属性authFilter也就是UsernamePasswordAuthenticationFilter做一些配置 @Override public void configure(B http) throws Exception { PortMapper portMapper = http.getSharedObject(PortMapper.class); if (portMapper != null) { authenticationEntryPoint.setPortMapper(portMapper); } RequestCache requestCache = http.getSharedObject(RequestCache.class); if (requestCache != null) { this.defaultSuccessHandler.setRequestCache(requestCache); } //这个filter就是构造方法中传过来的UsernamePasswordAuthenticationFilter, //在这个filter中会有一个authenticationManager属性,用来验证用户名和密码是否正确 //这个属性就是在这里赋值的,从共享对象中取出AuthenticationManager authFilter.setAuthenticationManager(http .getSharedObject(AuthenticationManager.class)); authFilter.setAuthenticationSuccessHandler(successHandler); authFilter.setAuthenticationFailureHandler(failureHandler); if (authenticationDetailsSource != null) { authFilter.setAuthenticationDetailsSource(authenticationDetailsSource); } SessionAuthenticationStrategy sessionAuthenticationStrategy = http .getSharedObject(SessionAuthenticationStrategy.class); if (sessionAuthenticationStrategy != null) { authFilter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy); } RememberMeServices rememberMeServices = http .getSharedObject(RememberMeServices.class); if (rememberMeServices != null) { authFilter.setRememberMeServices(rememberMeServices); } //这是在后处理器对authFilter进行加工,后处理是由使用者提供来对authFilter做进一步处理, //大部分情况都没有处理器 F filter = postProcess(authFilter); //把authFilter添加到HttpSecurity中,最终会被加到DefaultSecurityFilterChain中 http.addFilter(filter); }

上边提到了从SharedObject中获取AuthenticationManager的对象,那么它是在什么时候放进SharedObject的呢?

是在HttpSecurity的beforeConfigure方法中添加进去的

HttpSecurity

@Override protected void beforeConfigure() throws Exception { //实际是调用了AuthenticationManagerBuilder.build方法来生成对象, //这个AuthenticationManagerBuilder也实现了SecurityBuilder,也是一个构件器 setSharedObject(AuthenticationManager.class, getAuthenticationRegistry().build()); } private AuthenticationManagerBuilder getAuthenticationRegistry() { return getSharedObject(AuthenticationManagerBuilder.class); }

关于AuthenticationManager的作用,可以看这一篇详细了解下 spring seurity表单认证的原理



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3